﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nanoCMS.Core;
using nanoCMS.Core.Membership;
using nanoCMS.Forum.Concrete;
using nanoCMS.Forum.Config;
using nanoCMS.Forum.Providers;
using nanoCMS.Forum.WebUI.Models.AdminViewModels;
using nanoCMS.Forum.WebUI.Models.ViewModels;
using nanoCMS.Profiles.Providers;

namespace nanoCMS.Forum.WebUI.Models
{
    public class ForumService
    {
        private readonly WorldContext worldContext;
        private readonly ForumProvider forumProvider;
        private readonly ThreadProvider threadProvider;
        private readonly PostProvider postProvider;
        private readonly UserProvider userProvider;
        private readonly ForumUserInfoProvider infoProvider;
        private readonly ProfileProvider profileProvider;

        public ForumService(WorldContext worldContext)
        {
            this.worldContext = worldContext;
            this.forumProvider = new ForumProvider(this.worldContext);
            this.threadProvider = new ThreadProvider(this.worldContext);
            this.postProvider = new PostProvider(this.worldContext);
            this.userProvider = worldContext.UserProvider;
            this.infoProvider = new ForumUserInfoProvider(this.worldContext);   
            // todo - provicer from worldcontext
            this.profileProvider = new ProfileProvider(this.worldContext); 
        }

        #region Forum
        public ForumIndexVM BuildForumIndexModel()
        {
            // get root list (categories)
            List<ForumVM> rootList = forumProvider.GetRootForumList().Select(
                VMFactory.ForumVMFactory).ToList();
            // fill categories
            foreach (var subForum in rootList)
            {
                subForum.SubForums = forumProvider.GetSubForums(subForum.ForumID).Select(
                    VMFactory.ForumVMFactory).ToList();
            }
            // build model
            ForumIndexVM model = new ForumIndexVM
            {
                RootList = rootList
            };
            return model;
        }

        public ExplorerVM BuildForumExplorerModel(int forumID, int page)
        {
            int pageIndex = page < 1 ? 0 : (page - 1);
            Concrete.Forum forumEntity = forumProvider.GetForum(forumID);
            if (forumEntity == null)
            {
                throw new Exception(String.Format("No forum with ForumID = {0}.", forumID));
            }
            ForumVM forumVM = VMFactory.ForumVMFactory(forumEntity);
            forumVM.SubForums = forumProvider.GetSubForums(forumID).Select(
                VMFactory.ForumVMFactory).ToList();
            List<ThreadVM> threadsVM = threadProvider.GetForumThreads(forumID, pageIndex, ForumConfig.ThreadsPerPage).Select(
                VMFactory.ThreadVMFactory).ToList();
            // fill threads last post id
            foreach (var threadVM in threadsVM)
            {
                if(threadVM.LastPostId > 0)
                {
                    var lastPost = postProvider.GetPost(threadVM.LastPostId); 
                    if(lastPost != null)
                    {
                        threadVM.LastPostTime = lastPost.PostTime; 
                        Core.Concrete.User lastPostUser = null; 
                        if(userProvider.GetUser(lastPost.UserID, out lastPostUser) == MembershipReturn.Success)
                        {
                            if(lastPostUser != null)
                            {
                                threadVM.LastPostUser = lastPostUser.GetDisplayName();
                            }
                            else
                            {
                                threadVM.LastPostUser = "---"; 
                            }
                        }
                    }
                } 
            }
            // build model
            ExplorerVM model = new ExplorerVM()
            {
                Forum = forumVM,
                Threads = threadsVM,
                Page = page
            };
            return model;
        }

        public void DeleteForum(int forumID)
        {
            // delete threads
            var forumThreads = threadProvider.GetThreads(forumID);
            foreach (var forumThread in forumThreads)
            {
                DeleteThread(forumThread.ThreadID);
            }
            // recursive call for subforums
            var subForums = forumProvider.GetSubForums(forumID);
            foreach (var subForum in subForums)
            {
                DeleteForum(forumID);
            }
            // finaly, delete forum
            forumProvider.DeleteForum(forumID);
        } 
        #endregion

        #region Thread 
        public EditThreadVM BuildCreateThreadModel(int forumID)
        {
            return new EditThreadVM()
            {
                ForumID = forumID,
                Thread = new ThreadVM(),
                Post = new PostVM()
            };
        }

        public EditThreadVM BuildEditThreadModel(int threadID)
        {
            ThreadVM thread = VMFactory.ThreadVMFactory(threadProvider.GetThread(threadID));
            PostVM post = VMFactory.PostVMFactory(postProvider.GetPost(thread.ThreadPostID));
            EditThreadVM model = new EditThreadVM()
            {
                ForumID = thread.ForumID,
                Thread = thread,
                Post = post
            };
            return model;
        }

        public ThreadVM CreateThread(EditThreadVM model)
        {
            // TODO check forum rights
            Thread insertedThread = threadProvider.CreateThread(worldContext.CurrentUserID, model.ForumID, model.Thread.Title,
                                                        model.Thread.Description, model.Thread.Title, model.Post.Content, DateTime.Now);
            infoProvider.IncreasePostCount(SessionManagerBase.CurrentUserID);
            return VMFactory.ThreadVMFactory(insertedThread); 
        }

        public ThreadVM EditThread(EditThreadVM model)
        {
            Thread thread = threadProvider.EditThread(model.Thread.ThreadID, model.Thread.Title,
                                                          model.Thread.Description, model.Thread.Title, model.Post.Content);
            return VMFactory.ThreadVMFactory(thread); 
        }

        public void DeleteThread(int threadID)
        {
            Thread thread = threadProvider.GetThread(threadID);
            if (thread != null)
            {
                // update forum
                if (thread.ForumID.HasValue)
                {
                    forumProvider.DecreaseForumThreadCount(thread.ForumID.Value);
                }
                // delete posts
                var threadPosts = postProvider.GetThreadPosts(threadID);
                foreach (var post in threadPosts)
                {
                    postProvider.DeletePost(post.PostID);
                    infoProvider.DecreasePostCount(post.UserID);
                }
                if (thread.ThreadPostID.HasValue)
                {
                    var threadPost = postProvider.GetPost(thread.ThreadPostID.Value);
                    infoProvider.DecreasePostCount(threadPost.UserID);
                    postProvider.DeletePost(threadPost.PostID);
                }
                // delete thread
                threadProvider.DeleteThread(threadID);
            }
        }

        public ThreadDisplayVM BuildThreadDisplayModel(int threadID, int page)
        {
            int pageIndex = page - 1;
            if (pageIndex < 0)
            {
                pageIndex = 0;
            }
            ThreadVM thread = VMFactory.ThreadVMFactory(threadProvider.GetThread(threadID));
            ForumVM parentForum = VMFactory.ForumVMFactory(forumProvider.GetForum(thread.ForumID));
            // get posts & fill user info
            List<PostVM> posts = postProvider.GetThreadPosts(threadID, pageIndex, ForumConfig.PostsPerPage).Select(
                VMFactory.PostVMFactory).ToList();
            List<PostDisplayVM> postDisplayList = new List<PostDisplayVM>(posts.Count);
            int i = 2; 
            foreach (var postVM in posts)
            {
                var userVM = getPostUser(postVM);
                var postDisplayEntry = new PostDisplayVM()
                {
                    Post = postVM,
                    User = userVM,
                    UserInfo = VMFactory.ForumUserInfoFactory(infoProvider.Get(postVM.UserID)), 
                    PostNumber = (pageIndex * ForumConfig.PostsPerPage + i++)
                }; 
                fillUserProfilePostData(postVM.UserID, ref postDisplayEntry);
                postDisplayList.Add(postDisplayEntry);
            }
            // thread display post
            PostVM post = VMFactory.PostVMFactory(postProvider.GetPost(thread.ThreadPostID));
            PostDisplayVM threadPostDisplay = new PostDisplayVM()
            {
                Post = post,
                User = getPostUser(post), 
                UserInfo = VMFactory.ForumUserInfoFactory(infoProvider.Get(post.UserID)),
                PostNumber = 1
            };
            fillUserProfilePostData(post.UserID, ref threadPostDisplay);
            // create & return model
            ThreadDisplayVM model = new ThreadDisplayVM()
            {
                Thread = thread,
                ThreadPost = threadPostDisplay,
                ThreadPosts = postDisplayList,
                Page = page,
                ParentForum = parentForum
            };
            return model;
        }

        public void ChangeLockThreadState(int threadID)
        {
            Thread thread = threadProvider.GetThread(threadID); 
            if(thread != null)
            {
                if(thread.IsClosed)
                {
                    threadProvider.UnlockThread(threadID);
                }
                else
                {
                    threadProvider.LockThread(threadID);
                }
            }
        }

        public void IncreaseThreadViews(int threadID)
        {
            threadProvider.IncreaseThreadViews(threadID);
        }

        private PostUserVM getPostUser(PostVM postVM)
        {
            Core.Concrete.User user = null;
            PostUserVM userVM = null;
            if (userProvider.GetUser(postVM.UserID, out user) == MembershipReturn.Success)
            {
                userVM = VMFactory.PostUserFactory(user);
            }
            else
            {
                userVM = new PostUserVM()
                {
                    DisplayName = "Anonymous"
                };
            }
            return userVM;
        }

        private void fillUserProfilePostData(int userId, ref PostDisplayVM post)
        {
            var personProfile = profileProvider.GetPersonProfile(userId); 
            if(personProfile != null)
            {
                post.Location = personProfile.City; 
                return;
            }
            var companyProfile = profileProvider.GetCompanyProfile(userId); 
            if(companyProfile != null)
            {
                post.Location = companyProfile.City;
                return;
            }
            // anyway, no profile
            post.Location = "Unknown"; // TODO - harcoded
        }

        #endregion

        #region Post
        public EditPostVM BuildCreatePostModel(int threadID)
        {
            ThreadVM thread = VMFactory.ThreadVMFactory(threadProvider.GetThread(threadID));
            EditPostVM model = new EditPostVM()
            {
                Thread = thread,
                Post = new PostVM()
            };
            return model;
        }

        public EditPostVM BuildEditPostModel(int postID)
        {
            // TODO check permission
            PostVM post = VMFactory.PostVMFactory(postProvider.GetPost(postID));
            if (post == null)
            {
                throw new Exception(String.Format("No post with PostID = {0}", postID));
            }
            ThreadVM thread = null;
            if (post.ThreadID != 0)
            {
                thread = VMFactory.ThreadVMFactory(threadProvider.GetThread(post.ThreadID));
            }
            else
            {
                thread = VMFactory.ThreadVMFactory(threadProvider.GetThreadByThreadPostID(post.PostID));
            }
            EditPostVM model = new EditPostVM()
            {
                Post = post,
                Thread = thread
            };
            return model;
        }

        public PostVM CreatePost(PostVM post)
        {
            // TODO check threadID
            Thread thread = threadProvider.GetThread(post.ThreadID);
            if (thread.IsClosed)
            {
                throw new Exception(string.Format("Thread {0} is closed.", thread.Title));
            }
            // TODO action sucess (CreatePost) ==> IncreaseThreadPosts
            Post newPost = postProvider.CreatePost(SessionManagerBase.CurrentUserID, post.ThreadID, post.Title, post.Content, DateTime.Now);
            if (newPost != null)
            {
                infoProvider.IncreasePostCount(SessionManagerBase.CurrentUserID);
                threadProvider.IncreaseThreadPostCount(post.ThreadID);
                threadProvider.SetThreadLastPostId(post.ThreadID, newPost.PostID);
                threadProvider.SetThreadLastPostTime(post.ThreadID, newPost.PostTime);
                // update forum post count
                if (thread.ForumID.HasValue)
                {
                    forumProvider.IncreaseForumPostCount(thread.ForumID.Value);
                }
            }
            return VMFactory.PostVMFactory(newPost); 
        }

        public Post EditPost(PostVM post)
        {
            // TODO check threadID
            // TODO admin can edit post in closed mode
            Thread thread = threadProvider.GetThread(post.ThreadID);
            if (thread.IsClosed)
            {
                throw new Exception(string.Format("Thread {0} is closed.", thread.Title));
            }
            // TODO check rights
            var returnPost = postProvider.EditPost(post.PostID, post.Title, post.Content);
            return returnPost; 
        }

        public void DeletePost(int postID)
        {
            Post post = postProvider.GetPost(postID);
            if (post != null)
            {
                // call before delete post // TODO - nicer
                if (post.ThreadID.HasValue && post.ThreadID.Value != 0)
                {
                    threadProvider.DecreaseThreadPostCount(post.ThreadID.Value);
                    // update forum post count
                    Thread thread = threadProvider.GetThread(post.ThreadID.Value);
                    if (thread.ForumID.HasValue)
                    {
                        forumProvider.DecreaseForumPostCount(thread.ForumID.Value);
                    }
                }
                postProvider.DeletePost(postID);
                // decrease user posts count
                infoProvider.DecreasePostCount(SessionManagerBase.CurrentUserID);
                // update last thread post
                if(post.ThreadID.HasValue)
                {
                    Post lastPost = postProvider.GetLastPost(post.ThreadID.Value);
                    if (lastPost != null)
                    {
                        threadProvider.SetThreadLastPostId(post.ThreadID.Value, lastPost.PostID);
                    }
                }
            }
        } 
        #endregion
    }
}
